package it.antonio.ilpizze.swing.util;

import it.antonio.ilpizze.swing.SwingException;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;

public class TextLayout implements LayoutManager2 {

	private Map<Component, Rectangle> components = new HashMap<Component, Rectangle>();
	private Map<Component, Mode> modes = new HashMap<Component, Mode>();
	
	Dimension gridSize;
	private String[] rows;
	
	private String text;

	public TextLayout(String text) {
		super();
		this.text = text;
		rows = text.split("\n");
		int gridWidth = rows[0].length()-1;
		int gridHeigth = rows.length;
		this.gridSize = new Dimension(gridWidth, gridHeigth);
	}

	@Override
	public void addLayoutComponent(Component component, Object c) {
		if (c instanceof String) {
			String constaintsString = (String) c;
			String constaints[] = constaintsString.split(" ");
			
			String cString = constaints[0];
			if (cString.length() > 1) {
				throw new SwingException("Constraint must be a single char string");
			}
			char constraint = cString.charAt(0);
			int x = -1;
			int y = -1;
			int width = -1;
			int heigth = -1;
			boolean startReadingHeaigth = false;
			for (int i = 0; i < rows.length; i++) {
				String row = rows[i];
				if (!startReadingHeaigth) {
					x = row.indexOf(constraint);
					y = i;
					if (x != -1) {
						for (int j = x; j < row.length(); j++) {
							width = 0;
							if (row.charAt(j) != constraint) {
								width = j - x;
								break;
							}
						}
						startReadingHeaigth = true;
						heigth = 1;
					}
				} else {
					if (row.charAt(x) == constraint) {
						heigth++;
					} else {
						break;
					}
				}
			}
			components.put(component, new Rectangle(x, y, width, heigth));
			
			if(constaints.length > 1){
				String mode = constaints[1];
				if(mode.equalsIgnoreCase("vs")){
					modes.put(component, Mode.VERTICAL_SCALE);
				} else 
				if(mode.equalsIgnoreCase("os")){
					modes.put(component, Mode.VERTICAL_SCALE);
				} else{ 
					throw new SwingException("Second constraint bust be vs or os");
				}
			} else {
				modes.put(component, Mode.NORMAL);
			}
			
		} 

	}

	@Override
	public void removeLayoutComponent(Component component) {
		components.remove(component);
	}

	@Override
	public void addLayoutComponent(String arg0, Component arg1) {
		throw new SwingException("Constraint must be a char");
	}

	public Dimension preferredLayoutSize(Container parent) {
		return getLayoutSize(parent, true);
	}

	public Dimension minimumLayoutSize(Container parent) {
		return getLayoutSize(parent, false);
	}

	protected Dimension getLayoutSize(Container parent, boolean isPreferred) {
		Dimension largestSize = getLargestCellSize(parent, isPreferred);
		Insets insets = parent.getInsets();
		largestSize.width = (largestSize.width * gridSize.width) + insets.left + insets.right;
		largestSize.height = (largestSize.height * gridSize.height) + insets.top + insets.bottom;
		return largestSize;
	}

	protected Dimension getLargestCellSize(Container parent, boolean isPreferred) {
		int ncomponents = parent.getComponentCount();
		Dimension maxCellSize = new Dimension(0, 0);
		for (int i = 0; i < ncomponents; i++) {
			Component c = parent.getComponent(i);
			Rectangle rect = components.get(c);
			if (c != null && rect != null) {
				Dimension componentSize;
				if (isPreferred) {
					componentSize = c.getPreferredSize();
				} else {
					componentSize = c.getMinimumSize();
				}
				// Note: rect dimensions are already asserted to be > 0 when the
				// component is added with constraints
				maxCellSize.width = Math.max(maxCellSize.width,	componentSize.width / rect.width);
				maxCellSize.height = Math.max(maxCellSize.height, componentSize.height / rect.height);
			}
		}
		return maxCellSize;
	}

	/**
	 * 0 -> sull'origine 0,5 -> centrato 1 -> opposto all'origine
	 */
	@Override
	public float getLayoutAlignmentX(Container arg0) {
		return 0;
	}

	/**
	 * 0 -> sull'origine 0,5 -> centrato 1 -> opposto all'origine
	 */
	@Override
	public float getLayoutAlignmentY(Container arg0) {
		return 0;
	}

	@Override
	public void invalidateLayout(Container arg0) {
		// nothing
	}

	@Override
	public Dimension maximumLayoutSize(Container arg0) {
		return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
	}

	@Override
	public void layoutContainer(Container container) {
		synchronized (container.getTreeLock()) {
			Insets insets = container.getInsets();
			int ncomponents = container.getComponentCount();

			if (ncomponents == 0) {
				return;
			}

			// Total parent dimensions
			Dimension size = container.getSize();
			int totalW = size.width - (insets.left + insets.right);
			int totalH = size.height - (insets.top + insets.bottom);
			
			
			for (int i = 0; i < ncomponents; i++) {
				Component c = container.getComponent(i);
				Rectangle rect = components.get(c);
				if (rect != null) {
					
					Mode mode = modes.get(c);
					// Cell dimensions, without padding
					int cellW = 0;
					int cellH = 0;
					
					switch(mode){
						case NORMAL:
							cellW = totalW / gridSize.width;
							cellH = totalH / gridSize.height;
							break;
						case VERTICAL_SCALE:
							cellW = totalW / gridSize.width;
							cellH = cellW;
							break;
						case ORIZONTAL_SCALE:
							cellH = totalH / gridSize.height;
							cellW = cellH;
							break;
					}
					
					int x = insets.left + (cellW * rect.x);
					int y = insets.top + (cellH * rect.y);
					int w = (cellW * rect.width);
					int h = (cellH * rect.height);
					
					// correction for lost pixels
					int widthOffset = totalW - (x+w);
					if(widthOffset < cellW){
						w += widthOffset;
					}
					int heigthOffset = totalH - (y+h);
					if(heigthOffset < cellH){
						h += heigthOffset;
					}
					
					
					c.setBounds(x, y, w, h);
				}
			}
		}
	}
	
	private enum Mode {
		VERTICAL_SCALE,
		ORIZONTAL_SCALE, 
		NORMAL
	}
	
	public String getLayoutText(){
		return text;
	}
}
